use crate::modal::sys_dept::SysDept;
use crate::modal::sys_group::SysGroup;
use crate::util::Config;
use chrono::{Local, NaiveDateTime};
use serde::{Deserialize, Serialize};
use sqlx::mysql::MySqlQueryResult;
use sqlx::{query, query_as, FromRow};
use void_log::*;

pub type SysUsers = Vec<SysUser>;

#[derive(Debug, Clone, Default, Serialize, Deserialize, FromRow)]
pub struct SysUser {
    pub id: Option<i64>,
    pub name: Option<String>,
    pub full_name: Option<String>,
    pub user_type: Option<i64>,
    pub email: Option<String>,
    pub phone: Option<String>,
    pub avatar: Option<String>,
    pub password: Option<String>,
    pub status: Option<i64>,
    pub del_flag: Option<i64>,
    pub login_ip: Option<String>,
    pub login_date: Option<NaiveDateTime>,
    pub pwd_update_date: Option<NaiveDateTime>,
    pub create_by: Option<String>,
    pub create_time: Option<NaiveDateTime>,
    pub update_by: Option<String>,
    pub update_time: Option<NaiveDateTime>,
    pub remark: Option<String>,
    #[sqlx(skip)]
    pub group: Option<SysGroup>,
    #[sqlx(skip)]
    pub dept: Option<SysDept>,
}

pub async fn list() -> SysUsers {
    let conn = Config::get_sqlx_pool().await;
    let sql = "select * from sys_user";
    query_as::<_, SysUser>(sql).fetch_all(&conn).await.unwrap()
}

pub async fn select(id: i64) -> SysUser {
    let conn = Config::get_sqlx_pool().await;
    let user_sql = "select * from sys_user where id = ?";
    let group_sql = "select a.* from sys_group a, sys_user_group b where a.id = b.group_id and b.user_id = ?";
    let dept_sql = "select a.* from sys_dept a, sys_user_dept b where a.id = b.dept_id and b.user_id = ?";
    let mut user_response = query_as::<_, SysUser>(user_sql).bind(id).fetch_one(&conn).await.unwrap();
    let group_response = query_as::<_, SysGroup>(group_sql).bind(id).fetch_one(&conn).await.unwrap();
    let dept_response = query_as::<_, SysDept>(dept_sql).bind(id).fetch_one(&conn).await.unwrap();
    user_response.group = Some(group_response);
    user_response.dept = Some(dept_response);
    user_response
}

pub async fn insert(sys_user: SysUser, dept_id: i64, group_id: i64) -> (u64, u64, u64) {
    let conn = Config::get_sqlx_pool().await;
    let password = bcrypt::hash(&sys_user.password.unwrap(), bcrypt::DEFAULT_COST).unwrap();
    let user_id = sys_user.id;
    let now = Local::now().naive_local();
    let sql = "insert into sys_user (name, full_name, user_type, email, phone, avatar, password, status, create_by, create_time, remark) values
    (?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?)";
    let group_sql = "insert into sys_user_group values(?, ?)";
    let dept_sql = "insert into sys_user_dept values(?, ?)";
    let res = query(sql)
        .bind(sys_user.name)
        .bind(sys_user.full_name)
        .bind(sys_user.user_type)
        .bind(sys_user.email)
        .bind(sys_user.phone)
        .bind(sys_user.avatar)
        .bind(password)
        .bind(sys_user.create_by)
        .bind(now)
        .bind(sys_user.remark).execute(&conn).await.unwrap();
    let group_res = query(group_sql).bind(user_id).bind(group_id).execute(&conn).await.unwrap();
    let dept_res = query(dept_sql).bind(user_id).bind(dept_id).execute(&conn).await.unwrap();
    (res.last_insert_id(), group_res.last_insert_id(), dept_res.last_insert_id())
}

pub async fn update(sys_user: SysUser) -> MySqlQueryResult {
    let conn = Config::get_sqlx_pool().await;
    let password = bcrypt::hash(&sys_user.password.unwrap(), bcrypt::DEFAULT_COST).unwrap();
    let now = Local::now().naive_local();
    let sql = "update sys_user set
    name = ?,
    full_name = ?,
    user_type = ?,
    email = ?,
    phone = ?,
    avatar = ?,
    password = ?,
    status = ?,
    pwd_update_date = ?,
    update_by = ?,
    update_time = ?,
    remark = ? where id = ?";
    query(sql)
        .bind(sys_user.name)
        .bind(sys_user.full_name)
        .bind(sys_user.user_type)
        .bind(sys_user.email)
        .bind(sys_user.phone)
        .bind(sys_user.avatar)
        .bind(password)
        .bind(sys_user.status)
        .bind(now)
        .bind(sys_user.update_by)
        .bind(now)
        .bind(sys_user.remark)
        .bind(sys_user.id).execute(&conn).await.unwrap()
}

pub async fn update_password(id: i64, new_pwd: &str) -> &'static str {
    // 判断
    let conn = Config::get_sqlx_pool().await;
    let user_sql = "select * from sys_user where id = ?";
    let user_response = query_as::<_, SysUser>(user_sql).bind(id).fetch_one(&conn).await.unwrap();
    if bcrypt::verify(new_pwd, &user_response.password.unwrap()).unwrap() {
        return "与原密码相同";
    }
    // 修改
    let new_password = bcrypt::hash(new_pwd, bcrypt::DEFAULT_COST).unwrap();
    let now = Local::now().naive_local();
    let sql = "update sys_user set password = ?, pwd_update_date = ? where id = ?";
    let response = query(sql).bind(new_password).bind(now).bind(id).execute(&conn).await.unwrap();
    match response.rows_affected() {
        0 => { "未修改" }
        _ => { "修改成功" }
    }
}

pub async fn delete_group(user_id: i64) -> MySqlQueryResult {
    let conn = Config::get_sqlx_pool().await;
    let sql = "delete from sys_user_group where user_id = ?";
    query(sql).bind(user_id).execute(&conn).await.unwrap()
}

pub async fn delete_dept(user_id: i64) -> MySqlQueryResult {
    let conn = Config::get_sqlx_pool().await;
    let sql = "delete from sys_user_dept where user_id = ?";
    query(sql).bind(user_id).execute(&conn).await.unwrap()
}

pub async fn delete(id: i64) -> MySqlQueryResult {
    delete_group(id).await;
    delete_dept(id).await;
    let conn = Config::get_sqlx_pool().await;
    let sql = "delete from sys_user where id = ?";
    query(sql).bind(id).execute(&conn).await.unwrap()
}

pub async fn login_check(username: &str, password: &str) -> (bool, &'static str) {
    let conn = Config::get_sqlx_pool().await;
    let sql = "select * from sys_user where del_flag < 1 name = ?";
    let response = query_as::<_, SysUser>(sql).bind(username).fetch_one(&conn).await;
    match response {
        Ok(user) => {
            if !bcrypt::verify(password, &user.password.unwrap()).unwrap() {
                log_warn!("密码错误");
                return (false, "用户名或密码错误");
            }
            if &user.status.unwrap() < &1 {
                log_warn!("账户被锁定");
                return (false, "账户被锁定");
            }
            log_info!("登录成功");
            (true, "登录成功")
        }
        Err(e) => {
            log_warn!("{e}");
            (false, "用户名或密码错误")
        }
    }
}